package javawdw.lang.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 反射工具类
 * @author 王大卫
 *
 * @param <T>
 */
@SuppressWarnings("unchecked")
public abstract class Reflect<T> extends Generic<T> {
	private Map<String, Field> fieldMap;
	private Map<String, Map<Class<?>[], Method>> methodMap;
	private Map<Class<?>[], Constructor<T>> constructorMap;
	private Field[] fields;
	private Method[] methods;
	private Constructor<T>[] constructors;
	
	public Field getField(String name) throws NoSuchFieldException {
		if (fieldMap != null) {
			return fieldMap.get(name);
		} else {
			Field field = getGenericClass().getDeclaredField(name);
			field.setAccessible(true);
			return field;
		}
	}

	public Method getMethod(String name, Class<?>... clas)
			throws NoSuchMethodException {
		Map<Class<?>[], Method> map = null;
		if (methodMap != null && (map = methodMap.get(name)) != null) {
			return map.get(clas);
		} else {
			Method method = getGenericClass().getDeclaredMethod(name, clas);
			method.setAccessible(true);
			return method;
		}
	}

	public Constructor<T> getConstructor(Class<?>... clas)
			throws NoSuchMethodException {
		if (constructorMap != null) {
			return constructorMap.get(clas);
		} else {
			Constructor<T> cons = getGenericClass().getDeclaredConstructor(clas);
			cons.setAccessible(true);
			return cons;
		}
	}

	public Field[] getFields() {
		if (fieldMap == null) {
			fieldMap = new HashMap<String, Field>();
			Field[] fields = getGenericClass().getDeclaredFields();
			for (int i = 0; i < fields.length; i++) {
				fields[i].setAccessible(true);
				fieldMap.put(fields[i].getName(), fields[i]);
			}
			this.fields = new Field[fieldMap.size()];
			fieldMap.values().toArray(this.fields);
		}
		return fields;
	}

	public Method[] getMethods() {
		if (methodMap == null) {
			methodMap = new HashMap<String, Map<Class<?>[],Method>>();
			Method[] methods = getGenericClass().getDeclaredMethods();
			for (int i = 0; i < methods.length; i++) {
				methods[i].setAccessible(true);
				Map<Class<?>[],Method> map = methodMap.get(methods[i].getName());
				if (map == null) {
					map = new HashMap<Class<?>[], Method>();
					methodMap.put(methods[i].getName(), map);
				}
				map.put(methods[i].getParameterTypes(), methods[i]);
			}
			this.methods = new Method[methods.length];
			Collection<Map<Class<?>[],Method>> col = methodMap.values();
			List<Method> results = new ArrayList<Method>();
			for (Map<Class<?>[],Method> m: col) {
				results.addAll(m.values());
			}
			results.toArray(this.methods);
		}
		return methods;
	}

	public Constructor<T>[] getConstructors() {
		if (constructorMap == null) {
			constructorMap = new HashMap<Class<?>[], Constructor<T>>();
			Constructor<T>[] constructors = (Constructor<T>[]) getGenericClass().getDeclaredConstructors();
			for (int i = 0; i < constructors.length; i++) {
				constructors[i].setAccessible(true);
				constructorMap.put(constructors[i].getParameterTypes(), constructors[i]);
			}
			this.constructors = new Constructor[constructors.length];
			constructorMap.values().toArray(this.constructors);
		}
		return constructors;
	}
}